﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FlanProject.Combats.Actions;

namespace FlanProject.Combats
{
    public class ArtificialIntelligence
    {
        public CombatCharacter Character { get; private set; }
        public List<CombatCharacter> Targets { get; set; }

        private List<CombatAction> offensiveActions = new List<CombatAction>();
        private List<CombatAction> defensiveActions = new List<CombatAction>();
        private List<CombatAction> neutralActions = new List<CombatAction>();

        public static Random actionSelection = new Random();

        public ArtificialIntelligence(CombatCharacter character)
        {
            if (character == null)
                throw new ArgumentNullException("Unknown character detected.");

            Character = character;
            Targets = new List<CombatCharacter>();
            GenerateAllActions();
        }

        public bool AreEnemiesAttacking
        {
            get
            {
                foreach (var target in Targets)
                {
                    if (target.CurrentState == CombatCharacter.States.Attacking)
                        return true;
                }

                return false;
            }
        }

        #region Action Generation

        private void GenerateAllActions()
        {
            offensiveActions.Clear();
            defensiveActions.Clear();
            neutralActions.Clear();

            GenerateMeleeAction();
            GenerateDefendAction();
            GenerateNeutralAction();
        }

        private void GenerateMeleeAction()
        {
            offensiveActions.Add(new MeleeCombatAction(Character));
        }

        private void GenerateDefendAction()
        {
            defensiveActions.Add(new DefenseCombatAction(Character));
        }

        private void GenerateNeutralAction()
        {
            neutralActions.Add(new RandomWalkCombatAction(Character));
        }

        #endregion

        #region Action Selection

        public CombatAction ChooseAction()
        {
            var random = new Random();
            var combatAction = (CombatAction)null;

            // Defense based on defense rate and enemies' current state
            if ((Character.Stats.DefendPercentage > 0) && defensiveActions.Count > 0
                && (actionSelection.Next(0, 100) < Character.Stats.DefendPercentage) && AreEnemiesAttacking)
                combatAction = ChooseDefensiveAction();

            // If no action selected, randomly select an offensive action or a random walk action
            combatAction = combatAction ?? (actionSelection.Next(0, 2) == 0 ? ChooseOffensiveAction() : ChooseNeutralAction());

            if (combatAction != null)
                combatAction.Reset();

            return combatAction;
        }

        public CombatAction ChooseOffensiveAction()
        {
            // Be sure that there is a valid combat in progress
            if ((Targets == null) || (Targets.Count <= 0))
                return null;

            var targetIndex = 0;

            do
            {
                targetIndex = (new Random()).Next(Targets.Count);
            }
            while (Targets[targetIndex].IsDeadOrDying);

            var target = Targets[targetIndex];

            foreach (var action in offensiveActions)
            {
                if (action.IsCharacterValidUser)
                {
                    action.Target = target;
                    return action;
                }
            }

            return null;
        }

        public CombatAction ChooseDefensiveAction()
        {
            if ((Targets == null) || (Targets.Count <= 0))
                return null;

            var target = (CombatCharacter)null;

            foreach (var enemy in Targets)
            {
                // Block attacking enemy near character
                if (enemy.CurrentState == CombatCharacter.States.Attacking)
                {
                    if (enemy.Position.X > Character.Position.X - 20 && enemy.Position.X < Character.Position.X + 20)
                        target = enemy;
                }
            }

            // If there is no target, then don't do anything
            if (target == null)
                return null;

            Console.WriteLine("Defense !");

            foreach (var action in defensiveActions)
            {
                if (action.IsCharacterValidUser)
                {
                    // Defend against the one attacking
                    action.Target = target;
                    action.Reset();
                    return action;
                }
            }

            return null;
        }

        public CombatAction ChooseNeutralAction()
        {
            if ((Targets == null) || (Targets.Count <= 0))
                return null;

            var target = (CombatCharacter)null;
            var leastHealthAmount = Int32.MaxValue;

            // Face the target with the lowest health points
            foreach (var enemy in Targets)
            {
                if (!enemy.IsDeadOrDying)
                {
                    var targetHealthPoints = enemy.Stats.CurrentHealthPoints;

                    if ((targetHealthPoints <= enemy.Stats.MaxHealthPoints) && (targetHealthPoints < leastHealthAmount))
                    {
                        target = enemy;
                        leastHealthAmount = targetHealthPoints;
                    }
                }
            }

            // If there is no target, then don't do anything
            if (target == null)
                return null;

            foreach (var action in neutralActions)
            {
                if (action.IsCharacterValidUser)
                {
                    action.Target = target;
                    return action;
                }
            }

            return null;
        }

        #endregion
    }
}
